#include "PacmanEnvironment.h"
#include "Common.h"

#include <iostream>
#include <cassert>
#include <cstring>

#define GAME_ROM_SIZE		0x4000
#define VIDEO_MEMORY_SIZE	0x0800
#define RAM_SIZE			0x07F0
#define SPRITE_DATA_SIZE	0x0010
#define IO_MEMORY_SIZE		0x0100

#define MEMORY_SIZE (GAME_ROM_SIZE + VIDEO_MEMORY_SIZE + RAM_SIZE + SPRITE_DATA_SIZE + IO_MEMORY_SIZE)

PacmanEnvironment::PacmanEnvironment()
{
	m_memory = new byte_t [MEMORY_SIZE];

	LoadRomFile("rom/pacman.6e", m_memory + 0x0000, 0x1000);
	LoadRomFile("rom/pacman.6f", m_memory + 0x1000, 0x1000);
	LoadRomFile("rom/pacman.6h", m_memory + 0x2000, 0x1000);
	LoadRomFile("rom/pacman.6j", m_memory + 0x3000, 0x1000);

	m_cpu		= new CpuZ80(this);
	m_videoHw	= new VideoHardware(m_memory + 0x4000, m_memory + 0x4FF0, m_memory + 0x5060);
	m_soundHw	= new SoundHardware(m_memory + 0x5040);

	m_cpu->Reset(0x0000, GAME_ROM_SIZE + VIDEO_MEMORY_SIZE + RAM_SIZE);
	
	m_isRunning			= true;
	m_interruptEnabled	= false;

	// Default Dip-Switch Settings (I guess)
	m_dipSwitch = CoinsPerGame_1Coin | NumberOfLives_3Lives | BonusScore_15000 | Difficulty_Normal | GhostNames_Normal;
}

PacmanEnvironment::~PacmanEnvironment()
{
	delete m_cpu;
	delete m_videoHw;
	delete m_soundHw;
	delete[] m_memory;
}

void PacmanEnvironment::Update(size_t cycles)
{
	m_cpu->Run(cycles);
	
	if (m_interruptEnabled)
		m_cpu->RequestInterrupt(m_interruptVector);

	m_videoHw->Update();
}

void PacmanEnvironment::SetInput(byte_t inputs[2])
{
	m_input0 = inputs[0];
	m_input1 = inputs[1];
}

byte_t PacmanEnvironment::ReadByte(word_t address) const
{
	if (address >= 0x5000 && address < 0x5040)
	{
		return m_input0;
	}
	else
	if(address >= 0x5040 && address < 0x5080)
	{
		return m_input1;
	}
	else
	if (address >= 0x5080 && address < 0x50C0)
	{
		// Dip-Switch
		return m_dipSwitch;
	}
	else
	if (address >= 0x50C0 && address < 0x5100)
	{
		assert(false);
		return 0x00;
	}
	else 
	if (address < MEMORY_SIZE)
	{
		return m_memory[address];
	}
	else
	{
		// Should never get here
		assert(false);
		return 0x00;
	}
}

void PacmanEnvironment::WriteByte(byte_t data8, word_t address)
{
	address &= 0x7FFF;

	if (address == 0x5000) 
	{
		// Harware Interrupt Enable
		m_interruptEnabled = (data8 & 1) > 0;
	}
	else
	if (address == 0x5001) 
	{
		// Sound Enable
		m_soundHw->SetEnabled((data8 & 1) > 0);
	}
	else
	if (address == 0x5002) 
	{
		// AUX Board Enable
	}
	else
	if (address == 0x5003) 
	{
		// Flip Screen
	}
	else
	if (address == 0x5004)
	{
		 // Player 1 Start Light
	}
	else
	if (address == 0x5005) 
	{
		// Player 2 Start Light
	}
	else
	if (address == 0x5006) 
	{
		// Coin Lock-Out
	}
	else
	if (address == 0x5007)
	{
		// Coint Counter
	}
	else
	if (address >= 0x5040 && address < 0x5060) 
	{
		// Sound Registers
		m_memory[address] = data8;
	}
	else
	if (address == 0x50C0) 
	{
		// Watchdog
	}
	else
	if (address >= 0x4000 && address < MEMORY_SIZE)
	{
		m_memory[address] = data8;
	}
	else
	{
		assert(false);
		printf("Write: %04X: %02X\n", address, data8);
	}
}

byte_t PacmanEnvironment::ReadBytePort(byte_t port) const
{
	IGNORE_PARAM(port);
	assert(false);
	return 0;
}

void PacmanEnvironment::WriteBytePort(byte_t data, byte_t port)
{
	if (port == 0x00)
	{
		m_interruptVector = data;
	}
	else
	{
		assert(false);
	}
}

void PacmanEnvironment::Play(sword_t * buffer, size_t size, size_t sampleRate)
{
	memset(buffer, 0, sizeof(sword_t) * size);
	m_soundHw->Play(buffer, size, sampleRate);
}